package com.gobrs.async.core.threadpool;

import com.gobrs.async.core.common.constant.RejectedExecutionHandlerConstant;
import com.gobrs.async.core.common.def.DefaultConfig;
import com.gobrs.async.core.common.exception.GobrsAsyncException;

import java.math.BigDecimal;
import java.util.concurrent.*;

/**
 * 线程池构建者
 */
public class ThreadPoolBuilder {

    /**
     * number of core threads
     * 核心线程数
     */
    private int corePoolSize = calculateCoreNum();

    /**
     * maximum number of threads
     * 最大线程数
     */
    private int maxPoolSize = corePoolSize + (corePoolSize >> 1);

    /**
     * thread survival time
     * 线程存活时间
     */
    private long keepAliveTime = 30000L;

    /**
     * thread survival time unit
     * 线程存活时间单位
     */
    private TimeUnit timeUnit = TimeUnit.MILLISECONDS;

    /**
     * Thread execution timeout
     * 线程执行超时
     */
    private long executeTimeOut = 10000L;

    /**
     * queue maximum capacity
     * 队列最大容量
     */
    private int capacity = 512;

    /**
     * blocking queue
     * 阻塞队列
     */
    private BlockingQueue workQueue = new LinkedBlockingQueue(capacity);

    /**
     * Reject com.gobrs.async.com.gobrs.async.test.task policy when thread pool com.gobrs.async.com.gobrs.async.test.task is full
     * 当线程池请求已满时拒绝请求策略
     */
    private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();

    /**
     * Whether to guard the thread
     * 是否为保护线程
     */
    private boolean isDaemon = false;

    /**
     * thread name prefix
     * 线程名前缀
     */
    private String threadNamePrefix;

    /**
     * Allow core threads to time out
     * 允许核心线程超时
     */
    private Boolean allowCoreThreadTimeOut = false;

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public ThreadPoolBuilder corePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
        return this;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public ThreadPoolBuilder maxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
        return this;
    }

    public long getKeepAliveTime() {
        return keepAliveTime;
    }

    public ThreadPoolBuilder keepAliveTime(long keepAliveTime) {
        this.keepAliveTime = keepAliveTime;
        return this;
    }

    public TimeUnit getTimeUnit() {
        return timeUnit;
    }

    public ThreadPoolBuilder timeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
        return this;
    }

    public long getExecuteTimeOut() {
        return executeTimeOut;
    }

    public ThreadPoolBuilder executeTimeOut(long executeTimeOut) {
        this.executeTimeOut = executeTimeOut;
        return this;
    }

    public int getCapacity() {
        return capacity;
    }

    public ThreadPoolBuilder capacity(int capacity) {
        this.capacity = capacity;
        return this;
    }

    public BlockingQueue getWorkQueue() {
        return workQueue;
    }

    public ThreadPoolBuilder workQueue(BlockingQueue workQueue) {
        this.workQueue = workQueue;
        return this;
    }

    public RejectedExecutionHandler getRejectedExecutionHandler() {
        return rejectedExecutionHandler;
    }

    public ThreadPoolBuilder rejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
        this.rejectedExecutionHandler = rejectedExecutionHandler;
        return this;
    }

    /**
     * 是否守护进程
     */
    public boolean isDaemon() {
        return isDaemon;
    }

    public ThreadPoolBuilder daemon(boolean daemon) {
        isDaemon = daemon;
        return this;
    }

    public String getThreadNamePrefix() {
        return threadNamePrefix;
    }

    public ThreadPoolBuilder threadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
        return this;
    }

    public Boolean getAllowCoreThreadTimeOut() {
        return allowCoreThreadTimeOut;
    }

    public ThreadPoolBuilder allowCoreThreadTimeOut(Boolean allowCoreThreadTimeOut) {
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        return this;
    }

    /**
     * 构建线程池执行器
     */
    public ThreadPoolExecutor build() {
        ThreadPoolExecutor executor;
        try {
            executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, timeUnit, workQueue, rejectedExecutionHandler);
        } catch (Exception exception) {
            throw new GobrsAsyncException(String.format("Thread Pool Config Error %s", exception));
        }
        executor.allowCoreThreadTimeOut(allowCoreThreadTimeOut);
        return executor;
    }

    /**
     * 由线程池执行器构建
     */
    public static ThreadPoolExecutor buildByThreadPool(ThreadPool pool) {
        check(pool);
        ThreadPoolExecutor executor;
        try {
            executor = new ThreadPoolExecutor(pool.getCorePoolSize(), pool.getMaxPoolSize(), pool.getKeepAliveTime(), pool.getTimeUnit(), pool.getWorkQueue(), caseReject(pool.getRejectedExecutionHandler()));
            executor.allowCoreThreadTimeOut(pool.getAllowCoreThreadTimeOut());
        } catch (Exception exception) {
            throw new GobrsAsyncException(String.format("Thread Pool Config Error %s", exception));
        }
        return executor;
    }

    private static void check(ThreadPool pool) {
        if (pool.getCorePoolSize() == null) {
            throw new GobrsAsyncException("thread pool coreSize empty");
        }
        if (pool.getMaxPoolSize() == null) {
            throw new GobrsAsyncException("thread pool maxSize empty");
        }
        if (pool.getKeepAliveTime() == null) {
            throw new GobrsAsyncException("thread pool keepAliveTime size empty");
        }
        if (pool.getCapacity() == null) {
            pool.setCapacity(DefaultConfig.THREADPOOLQUEUESIZE);
        }
    }

    /**
     * 匹配拒绝执行处理者
     */
    public static RejectedExecutionHandler caseReject(String rejected) {
        if (rejected == null) {
            return new ThreadPoolExecutor.AbortPolicy();
        }
        RejectedExecutionHandler rejectedExecutionHandler;
        switch (rejected) {
            case RejectedExecutionHandlerConstant.CALLER_RUNSPOLICY:
                rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
                break;
            case RejectedExecutionHandlerConstant.ABORT_POLICY:
                rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
                break;
            case RejectedExecutionHandlerConstant.DISCARD_POLICY:
                rejectedExecutionHandler = new ThreadPoolExecutor.DiscardPolicy();
                break;
            case RejectedExecutionHandlerConstant.DISCARDOLDEST_POLICY:
                rejectedExecutionHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
                break;
            default:
                rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
        }
        return rejectedExecutionHandler;
    }

    private Integer calculateCoreNum() {
        int cpuCoreNum = Runtime.getRuntime().availableProcessors();
        return new BigDecimal(cpuCoreNum).divide(new BigDecimal("0.2")).intValue();
    }

}
